package wsi.task;

import java.util.logging.Logger;

import org.rygh.core.html.Html;
import org.rygh.core.html.element.HtmlParagraph;
import org.rygh.core.util.UTime;
import org.rygh.semantic.gui.swing.input.Input;
import org.rygh.semantic.jena.SDataAccess;

import wsi.dataguide.DataGuideBuilder;
import wsi.detect.Caller;
import wsi.function.DirManager;
import wsi.function.Function;
import wsi.function.Function.Phrase;
import wsi.input.InputSelector;
import wsi.input.degree.QueryDegreeSelector;
import wsi.input.degree.SimpleDegreeSelector;
import wsi.input.feedback.FeedbackSelector;
import wsi.input.random.RandomSelector;
import wsi.match.FunctionalityXPath;
import wsi.match.SubtreeMatcher;
import wsi.resolve.FunctionDefineTask;
import wsi.resolve.ResolveController;
import wsi.resolve.depr.TripleGenerator;
import wsi.script.AppConfig;
import wsi.subgraph.ExtractWorker;
import wsi.task.input.InputLocator;
import wsi.task.map.OldMapper;
import wsi.task.typedetector.TypeDetector;

public class Engine {
	public enum InputSelectType {
		Random, Degree, DegreeQuery, Feedback
	}

	private final Logger log = Logger.getLogger(getClass().getName());

	private final InputSelectType inputSelectType = AppConfig.shared()
			.getInputSelectType();
	private String result = "";

	public Engine() {
	}

	public void process(Function function) {
		if (!function.isScheduled())
			return;

		System.out.printf("[%s] processing %s...\n", UTime.toHumanString(),
				function.getFullName());

		SDataAccess.clear();

		DirManager dir = new DirManager(function);

		// if (!function.isSkipped(Phrase.INPUT))
		result = taskInput(dir);

		if (result == null)
			return;

		// included by taskInput
		/*
		 * 13-12-04 do not include by input. there is no need to extract
		 * subgraphs when testing input selection module only.
		 */
		if (!function.isSkipped(Phrase.YAGO_HOP))
			result = taskSubgraph(dir);

		if (!function.isSkipped(Phrase.DATA_GUIDE))
			result = taskDataGuide(dir);

		if (!function.isSkipped(Phrase.TYPE)) {
			TypeDetector det = taskType(dir);
			InputLocator loc = taskLocateInput(dir);

			Html html = new Html(String.format("%s/%s/type_and_location",
					function.getService(), function.getName()));

			html.addElement(det.toHtmlTable());
			html.newline();
			html.addElement(loc.toHtmlTable());

			dir.saveToFunctionDir("type_and_location.html", html.toString());
		}

		if (!function.isSkipped(Phrase.MAPPING))
			result = taskMapping(dir);

		if (!function.isSkipped(Phrase.FUNC_XPATH)) {
			result = taskDataGuide(dir);
			result = taskFuncXpath(dir);
		}

		if (function.getRuntimeInfo().isAssistNeeded()) {
			if (!function.getRuntimeInfo().isAssistReady()) {
				// re-process if needs assist and assist is ready

				if (function.getRuntimeInfo().checkIfAssistReady()) {
					log.info("Re-process after being assisted.");
					process(function);
					return;
				} else {
					log.info("[warn] assist info should be ready.");
				}
			}
		}

		if (!function.isSkipped(Phrase.SUBTREE_MAPPING))
			result = taskSubtreeMapping(dir);

		if (!function.isSkipped(Phrase.RESOLVE))
			result = taskResolve(dir);

		if (!function.isSkipped(Phrase.TRIPLES))
			result = taskTriples(dir);

		if (!function.isSkipped(Phrase.FUNC_DEF))
			result = taskFuncDefinition(dir);

		if (!function.isSkipped(Phrase.MEASURE))
			result = taskMeasure(dir);

		SDataAccess.clear();
	}

	public String save() {
		return result;
	}

	private String taskDataGuide(DirManager dir) {
		System.out.println("dataguide");

		TaskRoutine dguide = new DataGuideBuilder(dir);
		dguide.compute();
		return dguide.save();
	}

	private String taskFuncDefinition(DirManager dir) {
		if (!dir.loadDataGuide())
			return "No dataguide";

		TaskRoutine tg = new FunctionDefineTask(dir);
		tg.compute();
		return tg.save();

	}

	private String taskFuncXpath(DirManager dir) {
		System.out.println("functionality of XPaths");

		if (!dir.loadDataGuide())
			return "No dataguide";

		TaskRoutine func = new FunctionalityXPath(dir);
		func.compute();
		return func.save();
	}

	private String taskInput(DirManager dir) {
		System.out.printf("[%s] prepare %d inputs by %s\n",
				UTime.toHumanString(), AppConfig.shared().getInputSize(),
				inputSelectType.name());

		InputSelector selector = null;

		switch (inputSelectType) {
		case Degree:
			selector = new SimpleDegreeSelector(dir);
			break;

		case DegreeQuery:
			selector = new QueryDegreeSelector(dir);
			break;

		case Feedback:
			selector = new FeedbackSelector(dir);
			break;

		case Random:
			selector = new RandomSelector(dir);
			break;
		}

		if (!selector.select())
			return null;

		Input[] inputs = selector.load();

		if (inputs == null)
			System.out.println("[warn] no inputs loaded.");
		else
			System.out.printf("[info] loaded %d inputs.\n", inputs.length);

		Caller caller = new Caller(dir);
		caller.call(inputs);

		// String res = taskSubgraph(dir);
		//
		// return res;

		HtmlParagraph para = new HtmlParagraph(inputs.length
				+ " inputs collected.");
		return para.toString();
	}

	private InputLocator taskLocateInput(DirManager dir) {
		if (!dir.loadDataGuide())
			return null;

		String name = dir.getFunction().getName();

		log.info("locating input for " + name);

		TaskRoutine locator = new InputLocator(dir.getDataGuide(), true);
		((InputLocator) locator).setInput(new Input(name));
		locator.compute();

		return (InputLocator) locator;
	}

	private String taskMapping(DirManager dir) {
		TaskRoutine mapper = new OldMapper(dir);
		mapper.compute();
		return mapper.save();

	}

	private String taskMeasure(DirManager dir) {
		if (!dir.loadDataGuide())
			return "No dataguide";

		// TaskRoutine mapper = new Calibrator(dir);
		// mapper.compute();
		// return mapper.save();

		return "";
	}

	private String taskResolve(DirManager dir) {
		System.out.println("resolving");

		if (!dir.loadDataGuide())
			return "No dataguide";

		TaskRoutine tg = new ResolveController(dir);
		tg.compute();
		((ResolveController) tg).define();

		return tg.save();
	}

	private String taskSubgraph(DirManager dir) {
		System.out.println("extracting subgraphs");

		ExtractWorker subgraph = new ExtractWorker(dir, null);
		subgraph.process();

		return subgraph.toString();
	}

	private String taskSubtreeMapping(DirManager dir) {
		if (!dir.loadDataGuide())
			return "No dataguide";

		TaskRoutine sm = new SubtreeMatcher(dir);
		sm.compute();
		((SubtreeMatcher) sm).computeStatistics();
		return sm.save();
	}

	private String taskTriples(DirManager dir) {
		if (!dir.loadDataGuide())
			return "No dataguide";

		TaskRoutine tg = new TripleGenerator(dir);
		tg.compute();
		return tg.save();
	}

	private TypeDetector taskType(DirManager dir) {
		if (!dir.loadDataGuide())
			return null;

		TaskRoutine detector = new TypeDetector(dir);
		detector.compute();
		// detector.save();
		// System.out.println(detector);

		return (TypeDetector) detector;
	}

}
